#include <cstring>
#include "ByteArray.h"

char sh::ByteArray::toHexLower(uint32_t value) noexcept
{
    return "0123456789abcdef"[value & 0xF];
}

char sh::ByteArray::toHexUpper(uint32_t value) noexcept
{
    return "0123456789ABCDEF"[value & 0xF];
}

int32_t sh::ByteArray::fromHex(uint32_t c) noexcept
{
    return ((c >= '0') && (c <= '9')) ? int(c - '0') :
           ((c >= 'A') && (c <= 'F')) ? int(c - 'A' + 10) :
           ((c >= 'a') && (c <= 'f')) ? int(c - 'a' + 10) :
           /* otherwise */              -1;
}

sh::ByteArray sh::ByteArray::fromHex(const std::string &hexEncoded) noexcept
{
    sh::ByteArray data;
    char ch = 0x00;
    for (int index = 0; index < hexEncoded.length() - 1; index++)
    {
        ch |= (char) fromHex(hexEncoded.at(index++)) << 4;
        ch |= (char) fromHex(hexEncoded.at(index));
        data.push_back(ch);
        ch = 0x00;
    }
    return data;
}

sh::ByteArray::ByteArray()
{
}
sh::ByteArray::ByteArray(const char *data, int32_t size)
{
    originData.append(data, size);
}
sh::ByteArray::ByteArray(const std::string &data)
{
    originData.append(data);
}
sh::ByteArray::ByteArray(int32_t size, char ch)
{
    originData.resize(size);
    std::fill(originData.begin(), originData.end(), ch);
}
sh::ByteArray::ByteArray(const sh::ByteArray &other)
{
    originData.append(other.data());
}
sh::ByteArray::~ByteArray()
{
    originData.clear();
}
sh::ByteArray &sh::ByteArray::append(const sh::ByteArray &ba)
{
    originData.append(ba.data());
    return *this;
}
sh::ByteArray &sh::ByteArray::append(const std::string &str)
{
    originData.append(str);
    return *this;
}
sh::ByteArray &sh::ByteArray::append(const char *str)
{
    originData.append(str);
    return *this;
}
sh::ByteArray &sh::ByteArray::append(const char *str, int32_t len)
{
    originData.append(str, len);
    return *this;
}
sh::ByteArray &sh::ByteArray::append(char ch)
{
    originData.push_back(ch);
    return *this;
}
char sh::ByteArray::at(int32_t index) const
{
    if (originData.size() <= index || index < 0)
    {
        return 0;
    }
    return originData.at(index);
}
int32_t sh::ByteArray::capacity() const
{
    return (int32_t) originData.capacity();
}
void sh::ByteArray::chop(int32_t n)
{
    originData.substr(0, originData.length() - n);
}
void sh::ByteArray::clear()
{
    originData.clear();
}
bool sh::ByteArray::contain(const sh::ByteArray &ba) const
{
    return originData.find(ba.data()) != std::string::npos;
}
bool sh::ByteArray::contain(const char *str) const
{
    return originData.find(str) != std::string::npos;
}
bool sh::ByteArray::contain(char ch) const
{
    return originData.find(ch) != std::string::npos;
}
int32_t sh::ByteArray::count() const
{
    return (int32_t) originData.size();
}
std::string sh::ByteArray::data() const
{
    return originData;
}
std::string &sh::ByteArray::data()
{
    return originData;
}
bool sh::ByteArray::endsWith(const sh::ByteArray &ba) const
{
    const std::string &str = ba.data();
    return originData.rfind(str) == (originData.length() - str.length());
}
bool sh::ByteArray::endsWith(const char *str) const
{
    size_t length = std::strlen(str);
    return originData.rfind(str) == (originData.length() - length);
}
bool sh::ByteArray::endsWith(char ch) const
{
    return originData.rfind(ch) == (originData.length() - 1);
}
sh::ByteArray &sh::ByteArray::fill(char ch, int32_t size)
{
    originData.resize(size);
    std::fill(originData.begin(), originData.end(), ch);
    return *this;
}
int32_t sh::ByteArray::indexOf(const sh::ByteArray &ba, int32_t from) const
{
    return (int32_t) originData.find(ba.data(), from);
}
int32_t sh::ByteArray::indexOf(const std::string &str, int32_t from) const
{
    return (int32_t) originData.find(str, from);
}
int32_t sh::ByteArray::indexOf(const char *str, int32_t from) const
{
    return (int32_t) originData.find(str, from);
}
int32_t sh::ByteArray::indexOf(char ch, int32_t from) const
{
    return (int32_t) originData.find(ch, from);
}
sh::ByteArray &sh::ByteArray::insert(int32_t i, const sh::ByteArray &ba)
{
    originData.insert(i, ba.data());
    return *this;
}
sh::ByteArray &sh::ByteArray::insert(int32_t i, const std::string &str)
{
    originData.insert(i, str);
    return *this;
}
sh::ByteArray &sh::ByteArray::insert(int32_t i, const char *str)
{
    originData.insert(i, str);
    return *this;
}
sh::ByteArray &sh::ByteArray::insert(int32_t i, const char *str, int32_t len)
{
    originData.insert(i, str, len);
    return *this;
}
sh::ByteArray &sh::ByteArray::insert(int32_t i, char ch, int32_t len)
{
    originData.insert(i, len, ch);
    return *this;
}
bool sh::ByteArray::isEmpty() const
{
    return originData.empty();
}
int32_t sh::ByteArray::lastIndexOf(const sh::ByteArray &ba, int32_t from) const
{
    return (int32_t) originData.rfind(ba.data(), from);
}
int32_t sh::ByteArray::lastIndexOf(const std::string &str, int32_t from) const
{
    return (int32_t) originData.rfind(str, from);
}
int32_t sh::ByteArray::lastIndexOf(const char *str, int32_t from) const
{
    return (int32_t) originData.rfind(str, from);
}
int32_t sh::ByteArray::lastIndexOf(char ch, int32_t from) const
{
    return (int32_t) originData.rfind(ch, from);
}
sh::ByteArray sh::ByteArray::left(int32_t len) const
{
    const std::string &str = originData.substr(0, len);
    return sh::ByteArray(str);
}
sh::ByteArray sh::ByteArray::leftJustified(int32_t width, char fill, bool truncate) const
{
    std::string str = originData.substr(0, width);
    if (truncate && str.length() < width)
    {
        str.append(width - str.length(), fill);
    }
    return sh::ByteArray(str);
}
int32_t sh::ByteArray::length() const
{
    return (int32_t) originData.length();
}
sh::ByteArray sh::ByteArray::mid(int32_t pos, int32_t len) const
{
    std::string substr = originData.substr(pos, len);
    return sh::ByteArray(substr);
}
sh::ByteArray &sh::ByteArray::prepend(const sh::ByteArray &ba)
{
    insert(0, ba);
    return *this;
}
sh::ByteArray &sh::ByteArray::prepend(const char *str)
{
    insert(0, str);
    return *this;
}
sh::ByteArray &sh::ByteArray::prepend(const char *str, int32_t len)
{
    insert(0, str, len);
    return *this;
}
sh::ByteArray &sh::ByteArray::prepend(char ch, int32_t len)
{
    insert(0, ch, len);
    return *this;
}
void sh::ByteArray::push_back(const sh::ByteArray &other)
{
    append(other);
}
void sh::ByteArray::push_back(const char *str)
{
    append(str);
}
void sh::ByteArray::push_back(char ch)
{
    append(ch);
}
void sh::ByteArray::push_front(const sh::ByteArray &other)
{
    prepend(other);
}
void sh::ByteArray::push_front(const char *str)
{
    prepend(str);
}
void sh::ByteArray::push_front(char ch, int32_t len)
{
    prepend(ch, len);
}
sh::ByteArray &sh::ByteArray::remove(int32_t pos, int32_t len)
{
    originData.replace(pos, len, "");
    return *this;
}
sh::ByteArray sh::ByteArray::repeated(int32_t times) const
{
    std::string target_str;
    for (int time = 1; time < times; time++)
    {
        target_str.append(originData);
    }
    return sh::ByteArray(target_str);
}
sh::ByteArray &sh::ByteArray::replace(int32_t pos, int32_t len, const sh::ByteArray &after)
{
    originData.replace(pos, len, after.originData);
    return *this;
}
sh::ByteArray &sh::ByteArray::replace(int32_t pos, int32_t len, const char *after)
{
    originData.replace(pos, len, after);
    return *this;
}
sh::ByteArray &sh::ByteArray::replace(int32_t pos, int32_t len, const char *after, int32_t alen)
{
    originData.replace(pos, len, after, len);
    return *this;
}
sh::ByteArray &sh::ByteArray::replace(const sh::ByteArray &before, const sh::ByteArray &after)
{
    replace(before.data(), after.data());
    return *this;
}
sh::ByteArray &sh::ByteArray::replace(const std::string &before, const std::string &after)
{
    std::string result;
    std::string::size_type pos_begin = 0;
    std::string::size_type pos = originData.find(before);
    while (std::string::npos != pos)
    {
        result.append(originData, pos_begin, pos - pos_begin);
        result.append(after);
        pos_begin = pos + before.length();
        pos = originData.find(before, pos_begin);
    }
    if (pos_begin < originData.length())
    {
        result.append(originData.begin() + pos_begin, originData.end());
    }

    originData = result;
    return *this;
}

void sh::ByteArray::reserve(int32_t size)
{
    originData.reserve(size);
}
void sh::ByteArray::resize(int32_t size)
{
    originData.resize(size);
}
sh::ByteArray sh::ByteArray::right(int32_t len) const
{
    std::string str = originData.substr(originData.length() - len);
    return sh::ByteArray(str);
}
sh::ByteArray sh::ByteArray::rightJustified(int32_t width, char fill, bool truncate) const
{
    std::string str = originData.substr(originData.length() - width);
    if (truncate && str.length() < width)
    {
        str.append(width - str.length(), fill);
    }
    return sh::ByteArray(str);
}
int32_t sh::ByteArray::size() const
{
    return originData.size();
}
bool sh::ByteArray::startsWith(const sh::ByteArray &ba) const
{
    return startsWith(ba.data().data());
}
bool sh::ByteArray::startsWith(const char *str) const
{
    size_t length = std::strlen(str);
    if (originData.length() < length)
    {
        return false;
    }
    return originData.substr(0, length) == str;
}
bool sh::ByteArray::startsWith(char ch) const
{
    if (originData.empty())
    {
        return false;
    }
    return originData.at(0) == ch;
}
void sh::ByteArray::swap(sh::ByteArray &other)
{
    std::swap(originData, other.data());
}
sh::ByteArray sh::ByteArray::toHex() const
{
    std::string result;
    for (char ch: originData)
    {
        result.push_back(toHexLower(ch >> 4));
        result.push_back(toHexLower(ch & 0xF));
    }
    return sh::ByteArray(result);
}
bool sh::ByteArray::operator!=(const std::string &str) const
{
    return originData == str;
}
sh::ByteArray &sh::ByteArray::operator+=(const sh::ByteArray &ba)
{
    originData.append(ba.data());
    return *this;
}
sh::ByteArray &sh::ByteArray::operator+=(const std::string &str)
{
    originData.append(str);
    return *this;
}
sh::ByteArray &sh::ByteArray::operator+=(const char *str)
{
    originData.append(str);
    return *this;
}
sh::ByteArray &sh::ByteArray::operator+=(char ch)
{
    originData.push_back(ch);
    return *this;
}
bool sh::ByteArray::operator<(const std::string &str) const
{
    return originData < str;
}
bool sh::ByteArray::operator<=(const std::string &str) const
{
    return originData <= str;
}
sh::ByteArray &sh::ByteArray::operator=(const sh::ByteArray &other)
{
    originData = other.data();
    return *this;
}
sh::ByteArray &sh::ByteArray::operator=(const char *str)
{
    originData = std::string(str);
    return *this;
}
bool sh::ByteArray::operator==(const std::string &str) const
{
    return str == originData;
}
bool sh::ByteArray::operator>(const std::string &str) const
{
    return originData > str;
}
bool sh::ByteArray::operator>=(const std::string &str) const
{
    return originData >= str;
}
char sh::ByteArray::operator[](int32_t i) const
{
    return originData[i];
}
char sh::ByteArray::operator[](uint32_t i) const
{
    return originData[i];
}
